package moltencore.game.loader;

import moltencore.game.CannonData;
import moltencore.game.collision.HorizontalLine;
import moltencore.game.collision.VerticalLine;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.SAXException;
import org.xml.sax.Attributes;
import moltencore.game.bubble.Type;
import spark.math.Vector2;

public class LevelParser extends DefaultHandler {
	public static final String ELEMENT_LEVEL = "level";
	public static final String ATTRIBUTE_NAME = "name";
	public static final String ELEMENT_CANNON_DATA = "cannonData";
	public static final String ELEMENT_CANNON_POSITION = "cannonPosition";
	public static final String ELEMENT_CANNON_NEXT_POSITION = "cannonNextPosition";
	public static final String ELEMENT_CANNON_TARGET = "cannonTarget";
	public static final String ELEMENT_SCORE_POSITION = "scorePosition";
	public static final String ELEMENT_RESULT_POSITION = "resultPosition";
	public static final String ELEMENT_NICK_POSITION = "nickPosition";
	public static final String ELEMENT_CLOCK_POSITION = "clockPosition";
	public static final String ATTRIBUTE_CANNON_INDEX = "index";
	public static final String ATTRIBUTE_X = "x";
	public static final String ATTRIBUTE_Y = "y";
	public static final String ELEMENT_WALL = "wall";
	public static final String ELEMENT_CRITICAL_AREA = "criticalArea";
	public static final String ELEMENT_KILL_AREA = "killArea";
	public static final String ELEMENT_RESTRICTED_AREA = "restrictedArea";
	public static final String ATTRIBUTE_TYPE = "type";
	public static final String ATTRIBUTE_STICKY = "sticky";
	public static final String ATTRIBUTE_X_START = "xStart";
	public static final String ATTRIBUTE_Y_START = "yStart";
	public static final String ATTRIBUTE_X_END = "xEnd";
	public static final String ATTRIBUTE_Y_END = "yEnd";
	public static final String ELEMENT_BUBBLE = "bubble";
	public static final String ELEMENT_PUSH_BUBBLE = "pushBubble";
	public static final String ELEMENT_FILL_BUBBLE = "fillBubble";
	public static final String ATTRIBUTE_GRID_X = "gridX";
	public static final String ATTRIBUTE_GRID_Y = "gridY";
	public static final String ELEMENT_LEVEL_SPRITE = "levelSprite";
	public static final String ATTRIBUTE_SPRITE_MAP = "spriteMap";
	public static final String ATTRIBUTE_THEME = "theme";
	public static final String ATTRIBUTE_ID = "id";
	public static final String ATTRIBUTE_POS_X = "xPos";
	public static final String ATTRIBUTE_POS_Y = "yPos";
	public static final String ATTRIBUTE_POS_Z = "zPos";
	public static final String ATTRIBUTE_ROT = "rotation";
	public static final String ATTRIBUTE_SCALE_X = "xScale";
	public static final String ATTRIBUTE_SCALE_Y = "yScale";

	protected Level level;
	private CannonData currentCannonData;

	public Level getParsedLevel() {
		return level;
	}

	@Override
	public void startDocument() throws SAXException {
		level = new Level();
	}

	@Override
	public void endDocument() throws SAXException {
		super.endDocument();
	}

	@Override
	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
		if(localName.equals(ELEMENT_LEVEL)) {
			level.setName(attributes.getValue(ATTRIBUTE_NAME));
		}
		else if(localName.equals(ELEMENT_CANNON_DATA)) {
			try {
				currentCannonData = new CannonData(
						Integer.parseInt(attributes.getValue(ATTRIBUTE_CANNON_INDEX)),
						new Vector2(), new Vector2(), new Vector2());
			}
			catch(NumberFormatException e) {
				throw new SAXException(e);
			}
		}
		else if(localName.equals(ELEMENT_CANNON_POSITION)) {
			try {
				currentCannonData.getPosition().copy(new Vector2(Float.parseFloat(attributes.getValue(ATTRIBUTE_X)),
				                                                 Float.parseFloat(attributes.getValue(ATTRIBUTE_Y))));
			}
			catch(NumberFormatException e) {
				throw new SAXException(e);
			}
		}
		else if(localName.equals(ELEMENT_CANNON_NEXT_POSITION)) {
			try {
				currentCannonData.getNextPosition().copy(new Vector2(Float.parseFloat(attributes.getValue(ATTRIBUTE_X)),
				                                                     Float.parseFloat(attributes.getValue(ATTRIBUTE_Y))));
			}
			catch(NumberFormatException e) {
				throw new SAXException(e);
			}
		}
		else if(localName.equals(ELEMENT_CANNON_TARGET)) {
			try {
				currentCannonData.getTarget().copy(new Vector2(Float.parseFloat(attributes.getValue(ATTRIBUTE_X)),
				                                               Float.parseFloat(attributes.getValue(ATTRIBUTE_Y))));
			}
			catch(NumberFormatException e) {
				throw new SAXException(e);
			}
		}
		else if(localName.equals(ELEMENT_SCORE_POSITION)) {
			try {
				level.setScorePosition(new Vector2(Float.parseFloat(attributes.getValue(ATTRIBUTE_X)),
				                                   Float.parseFloat(attributes.getValue(ATTRIBUTE_Y))));
			}
			catch(NumberFormatException e) {
				throw new SAXException(e);
			}
		}
		else if(localName.equals(ELEMENT_RESULT_POSITION)) {
			try {
				level.setResultPosition(new Vector2(Float.parseFloat(attributes.getValue(ATTRIBUTE_X)),
				                                    Float.parseFloat(attributes.getValue(ATTRIBUTE_Y))));
			}
			catch(NumberFormatException e) {
				throw new SAXException(e);
			}
		}
		else if(localName.equals(ELEMENT_NICK_POSITION)) {
			try {
				level.setNickPosition(new Vector2(Float.parseFloat(attributes.getValue(ATTRIBUTE_X)),
				                                  Float.parseFloat(attributes.getValue(ATTRIBUTE_Y))));
			}
			catch(NumberFormatException e) {
				throw new SAXException(e);
			}
		}
		else if(localName.equals(ELEMENT_CLOCK_POSITION)) {
			try {
				level.setClockPosition(new Vector2(Float.parseFloat(attributes.getValue(ATTRIBUTE_X)),
				                                   Float.parseFloat(attributes.getValue(ATTRIBUTE_Y))));
			}
			catch(NumberFormatException e) {
				throw new SAXException(e);
			}
		}
		else if(localName.equals(ELEMENT_WALL)) {
			String type = attributes.getValue(ATTRIBUTE_TYPE);
			try {
				if(type.equals(LineType.Horizontal.toString())) {
					level.add(new HorizontalLine(Float.parseFloat(attributes.getValue(ATTRIBUTE_Y_START)), Boolean.parseBoolean(attributes.getValue(ATTRIBUTE_STICKY))));
				}
				else if(type.equals(LineType.Vertical.toString())) {
					level.add(new VerticalLine(Float.parseFloat(attributes.getValue(ATTRIBUTE_X_START)), Boolean.parseBoolean(attributes.getValue(ATTRIBUTE_STICKY))));
				}
				else {
					throw new SAXException("Invalid wall type " + type);
				}
			}
			catch(NumberFormatException e) {
				throw new SAXException(e);
			}
		}
		else if(localName.equals(ELEMENT_CRITICAL_AREA)) {
			String type = attributes.getValue(ATTRIBUTE_TYPE);
			try {
				if(type.equals(LineType.Horizontal.toString())) {
					level.addCriticalArea(new HorizontalLine(Float.parseFloat(attributes.getValue(ATTRIBUTE_Y_START)), false));
				}
				else if(type.equals(LineType.Vertical.toString())) {
					throw new RuntimeException("Not implemented");
					//level.add(new VerticalLine(Float.parseFloat(attributes.getValue(ATTRIBUTE_X_START)), false));
				}
				else {
					throw new SAXException("Invalid wall type " + type);
				}
			}
			catch(NumberFormatException e) {
				throw new SAXException(e);
			}
		}
		else if(localName.equals(ELEMENT_KILL_AREA)) {
			String type = attributes.getValue(ATTRIBUTE_TYPE);
			try {
				if(type.equals(LineType.Horizontal.toString())) {
					level.addKillArea(new HorizontalLine(Float.parseFloat(attributes.getValue(ATTRIBUTE_Y_START)), false));
				}
				else if(type.equals(LineType.Vertical.toString())) {
					throw new RuntimeException("Not implemented");
					//level.add(new VerticalLine(Float.parseFloat(attributes.getValue(ATTRIBUTE_X_START)), false));
				}
				else {
					throw new SAXException("Invalid wall type " + type);
				}
			}
			catch(NumberFormatException e) {
				throw new SAXException(e);
			}
		}
		else if(localName.equals(ELEMENT_RESTRICTED_AREA)) {
			String type = attributes.getValue(ATTRIBUTE_TYPE);
			try {
				if(type.equals(LineType.Horizontal.toString())) {
					level.addRestrictedArea(new HorizontalLine(Float.parseFloat(attributes.getValue(ATTRIBUTE_Y_START)), false));
				}
				else if(type.equals(LineType.Vertical.toString())) {
					throw new RuntimeException("Not implemented");
					//level.add(new VerticalLine(Float.parseFloat(attributes.getValue(ATTRIBUTE_X_START)), false));
				}
				else {
					throw new SAXException("Invalid wall type " + type);
				}
			}
			catch(NumberFormatException e) {
				throw new SAXException(e);
			}
		}
		else if(localName.equals(ELEMENT_BUBBLE)) {
			String typeString = attributes.getValue(ATTRIBUTE_TYPE);
			Type type = null;
			if(typeString.equals(Type.Red.toString())) {
				type = Type.Red;
			}
			else if(typeString.equals(Type.Green.toString())) {
				type = Type.Green;
			}
			else if(typeString.equals(Type.Blue.toString())) {
				type = Type.Blue;
			}
			else if(typeString.equals(Type.Yellow.toString())) {
				type = Type.Yellow;
			}
			else if(typeString.equals(Type.Orange.toString())) {
				type = Type.Orange;
			}
			else if(typeString.equals(Type.Violet.toString())) {
				type = Type.Violet;
			}
			else if(typeString.equals(Type.Black.toString())) {
				type = Type.Black;
			}
			else if(typeString.equals(Type.White.toString())) {
				type = Type.White;
			}
			else if(typeString.equals(Type.Rainbow.toString())) {
				type = Type.Rainbow;
			}
			else if(typeString.equals(Type.Blazing.toString())) {
				type = Type.Blazing;
			}
			else if(typeString.equals(Type.Ghost.toString())) {
				type = Type.Ghost;
			}
			else if(typeString.equals(Type.Anchor.toString())) {
				type = Type.Anchor;
			}
			else {
				throw new SAXException("Unknown bubble type " + typeString);
			}
			try {
				level.addBubble(type,
				                Integer.parseInt(attributes.getValue(ATTRIBUTE_GRID_X)),
				                Integer.parseInt(attributes.getValue(ATTRIBUTE_GRID_Y)));
			}
			catch(NumberFormatException e) {
				throw new SAXException(e);
			}
		}
		else if(localName.equals(ELEMENT_PUSH_BUBBLE)) {
			try {
				level.addPushBubble(Integer.parseInt(attributes.getValue(ATTRIBUTE_GRID_X)),
				                    Integer.parseInt(attributes.getValue(ATTRIBUTE_GRID_Y)));
			}
			catch(NumberFormatException e) {
				throw new SAXException(e);
			}
		}
		else if(localName.equals(ELEMENT_FILL_BUBBLE)) {
			try {
				level.addFillBubble(Integer.parseInt(attributes.getValue(ATTRIBUTE_GRID_X)),
				                    Integer.parseInt(attributes.getValue(ATTRIBUTE_GRID_Y)));
			}
			catch(NumberFormatException e) {
				throw new SAXException(e);
			}
		}
	}

	@Override
	public void endElement(String uri, String localName, String qName) throws SAXException {
		if(localName.equals(ELEMENT_CANNON_DATA)) {
			level.setCannonPosition(currentCannonData.getPosition(), currentCannonData.getIndex());
			level.setCannonNextPosition(currentCannonData.getNextPosition(), currentCannonData.getIndex());
			level.setCannonTarget(currentCannonData.getTarget(), currentCannonData.getIndex());
		}
	}

	public static enum LineType {
		Horizontal("horizontal"),
		Vertical("vertical"),
		Any("any");
		private String type;

		private LineType(String type) {
			this.type = type;
		}

		@Override
		public String toString() {
			return type;
		}
	}
}
